﻿//
// TravWinView.cs:  View component of the MPVC pattern
// This component subscribes to TravModelView notifications of state changes and keeps the controller consistent with those changes.
//
//
// Copyright 2011-2012 Kasey Osborn (kasewick@Wordpress.com)
// Ms-PL license - Use and redistribute freely
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

using FileTreeSpace;
using ProgressSpace;
using TravPresenterAbstraction;
using TravModelViewSpace;
using TravWinControllerSpace;


namespace TravWinViewSpace
{
    static class WinProgram
    {
        internal static string[] Args;

        [STAThread]
        static void Main (string[] args)
        {
            WinProgram.Args = args;

            Application.EnableVisualStyles ();
            Application.SetCompatibleTextRenderingDefault (false);
            Application.Run (new TravWinController ());
        }
    }


    public class TravWinView : ITravPresenter
    {
        private TravWinController winController;
        private TravModelView TravView;
        private IDetailPresenter detailPresenter;

        #region CONSTRUCTORS

        public TravWinView (TravWinController controller, TravModelView travView)
        {
            this.winController = controller;
            this.TravView = travView;

            travView.Presenter = this;
            controller.Tag = (ITravPresenter) this;

            travView.AddressChanged += UpdateTextAddress;
            travView.AddressChanged += UpdateTreeAddress;
            travView.FolderOpened += ShowTree;
            travView.FolderClosed += CloseTree;
            travView.StatusChanged += UpdateStatus;
            travView.FolderCreated += CreateFolder;
            travView.FolderRenamed += RenameFolder;
            travView.FolderRenamed += AddressHandleDirRenamed;
            travView.FolderRemoved += RemoveFolder;
        }

        public IDetailPresenter DetailPresenterFactory (DetailModelView detailModelView)
        {
            detailPresenter = new DetailWinView (TravView, detailModelView, winController);
            detailModelView.Presenter = detailPresenter;
            return detailPresenter;
        }

        public ProgressModel ProgressDialogFactory (string title)
        {
            ProgressModel progress = new ProgressModel (title);
            ProgressDialogWinController dialog = new ProgressDialogWinController (progress);
            IProgressPresenter view = new ProgressDialogWinView (dialog, progress);

            progress.Presenter = view;

            return progress;
        }

        #endregion

        #region PRESENTERS

        public bool InvokeRequired () { return winController.InvokeRequired; }

        public void Invoke (Delegate del, object[] ob)
        {
            winController.Invoke (del, ob);
        }


        public void ShowMessage (string message, string caption)
        {
            MessageBoxOptions options = 0;
            if (winController.RightToLeft == RightToLeft.Yes)
                options = MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign;

            MessageBox.Show (winController, message, caption,
                MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1,
                options);
        }

        public bool ShowQuestion (string message, string caption)
        {
            MessageBoxOptions options = winController.RightToLeft != RightToLeft.Yes ? 0 :
                MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign;

            DialogResult result = MessageBox.Show (winController, message, caption,
                MessageBoxButtons.OKCancel, MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2, options);

            return result == DialogResult.OK;
        }

        public string AddressText { get { return winController.address.Text; } set { winController.address.Text = value; } }

        #endregion

        #region OBSERVERS

        private void UpdateTreeAddress ()
        {
            TreeNode newSelectedNode = null;

            if (!string.IsNullOrEmpty (TravView.CurrentDir))
            {
                DirPath newPath = new DirPath (TravView.Tree, TravView.CurrentDir);

                if (newPath != null)
                    newSelectedNode = GetTreeNode (newPath);
            }

            winController.folders.SelectedNode = newSelectedNode;
        }


        private void UpdateTextAddress ()
        {
            winController.address.Text = TravView.CurrentDir;
        }


        private void ShowTree (DirRoot root)
        {
            winController.folders.SuspendLayout ();

            // Traverse directory structure and build tree widget.
            int newLevel = 0;
            TreeNodeCollection nodes = winController.folders.Nodes;
            foreach (DirPath path in root)
            {
                string dirName = path.NodeName;
                if (path.Count - 1 > newLevel)
                {
                    nodes = nodes[nodes.Count - 1].Nodes;
                    ++newLevel;
                }
                else
                    while (path.Count - 1 < newLevel)
                    {
                        TreeNode parent = nodes[0].Parent.Parent;
                        nodes = parent.Nodes;
                        --newLevel;
                    }

                // Add to the widget.
                nodes.Add (dirName);
            }

            winController.folders.ResumeLayout ();
        }


        private void UpdateStatus ()
        {
            winController.status.Text = TravView.Status;
        }


        private void CloseTree ()
        {
            winController.address.Select ();

            TreeNode n = winController.folders.TopNode;
            if (n != null)
                n.Remove ();
        }


        private void AddressHandleDirRenamed (string oldPathName, string oldLeafName, string newLeafName)
        {
            int oldLen = oldPathName.Length;
            string oldAddr = winController.address.Text;
            int oldParentLen = oldPathName.Length - oldLeafName.Length - 1;

            if (oldAddr.Length == oldLen || oldAddr.Length > oldLen && oldAddr[oldParentLen] == '\\')
                if (oldAddr.StartsWith (oldPathName, true, null))
                {
                    string newAddr = oldPathName.Substring (0, oldParentLen + 1) + newLeafName + oldAddr.Substring (oldLen);
                    winController.address.Text = newAddr;
                }
        }


        private void CreateFolder (string newPathName, string newLeafName)
        {
            int consumed = -1;
            TreeNode node = GetTreeNode (newPathName, ref consumed);
            bool error = node == null || newPathName.Length - consumed != newLeafName.Length;

            if (!error)
                node.Nodes.Add (newLeafName);
        }


        private void RenameFolder (string oldPathName, string oldLeafName, string newLeafName)
        {
            TreeNode node = GetTreeNode (oldPathName);
            if (node != null)
                node.Text = newLeafName;
        }


        private void RemoveFolder (string oldPathName)
        {
            TreeNode node = GetTreeNode (oldPathName);
            if (node != null)
                node.Remove ();

            if (winController.address.Text == TravView.CurrentDir)
            {
                if (winController.address.Text.StartsWith (oldPathName))
                    winController.address.Text = "";
            }
        }

        #endregion

        #region OBSERVER HELPERS

        private TreeNode GetTreeNode (string pathName)
        {
            int consumed = -1;
            TreeNode node = GetTreeNode (pathName, ref consumed);
            if (consumed == pathName.Length)
                return node;
            else
                return null;
        }

        private TreeNode GetTreeNode (string pathName, ref int consumed)
        {
            TreeNode gotNode = null;
            consumed = 0;
            for (TreeNodeCollection nodes = winController.folders.Nodes; nodes.Count > 0; nodes = gotNode.Nodes)
            {
                for (TreeNode node = nodes[0]; ; node = node.NextNode)
                {
                    if (node == null)
                        return gotNode;

                    int p1 = consumed + node.Text.Length;

                    if ((p1 == pathName.Length || (p1 < pathName.Length && pathName[p1] == '\\'))
                        && node.Text == pathName.Substring (consumed, node.Text.Length))
                    {
                        consumed = p1;
                        if (p1 >= pathName.Length)
                            return node;

                        gotNode = node;
                        ++consumed;
                        break;
                    }
                }
            }
            return gotNode;
        }

        private TreeNode GetTreeNode (DirPath path)
        {
            TreeNode node = null;
            TreeNodeCollection searchNodes = winController.folders.Nodes;

            for (int level = 0; level < path.Count; ++level)
            {
                string s2 = path[level].DirName;

                for (int k = 0; ; ++k)
                {
                    if (k == searchNodes.Count)
                        return node;

                    string s1 = searchNodes[k].Text;
                    if (s1 == s2)
                    {
                        node = searchNodes[k];
                        searchNodes = node.Nodes;
                        break;
                    }
                }
            }

            return node;
        }

        #endregion
    }
}
